import rrdtool
import os
import sys

from mon_center_utils import decode
from singletonmixin import Singleton
import mon_center_logger
import logging

sys.path.append('/usr/local/rrdtool-1.2.23')
sys.path.append('/usr/local/rrdtool-1.2.23/lib')
sys.path.append('/usr/local/rrdtool-1.2.23/lib64/python2.4/site-packages')

logger = logging.getLogger('center.db_handler')

class MonDBException(Exception):
    pass

class MonDBHandler(Singleton):

    def __init__(self, root_dir):
        #self._db_host = db_host
        #self._db_port = db_port
        self._root_dir = str(root_dir)
        self._md_cash = {}

    def cd(self, path):
        try:
            os.chdir(path)
        except OSError, e:
            if e.errno == 2:
                os.makedirs(path)
                os.chdir(path)
            else:
                raise e

    def write(self, host, name, val):
        #print "writing to db...%s:%s:%s" % (host, name, val)

        dir_path = self._root_dir + os.sep + host
        if not os.path.exists(dir_path):
            try:
                os.makedirs(dir_path)
            except Error, e:
                if e.error != 17:  # if not because file already exists
                    raise e

        if name == "metadata":
            f_path = dir_path + os.sep + name
            f = file(f_path, 'w+')
            self._md_cash[host] = decode(val)
            f.write(val)
            f.close()
            logger.debug('created metadata for %s' % host)
        else:
            self.push_to_rrd(host, name, val)


    def read(self, host, name, stat='AVERAGE', step=5, start=None, end=None):
        if name == "metadata":
            if self._md_cash.has_key(host):
                val = self._md_cash[host]
            else:
                meta_path = self._root_dir + os.sep + host + \
                        os.sep + "metadata"
                f = file(meta_path, 'r')
                val = decode(f.read())
                self._md_cash[host] = val
                f.close()
        else: 
            # Read monitor data
            # The most recent data will be retrived if there is 
            # no time specified
            step = self.round_res(host, name, step)
            rrd_db = self._root_dir + os.sep + host + os.sep + name + ".rrd"
            if not os.path.exists(rrd_db):
                parent, fname = os.path.split(rrd_db)
                name = fname.rsplit('.', 1)[0] 
                raise MonDBException, 'metric %s not found' % name
            if start is None:
                val = self.rrd_fetch_latest(rrd_db, stat, step)
            else:
                val = self.rrd_fetch(rrd_db, stat, step, start, end)

        return val

    def round_res(self, host, name, res):
        return res

    def rrd_fetch_latest(self, rrd_db, cf, res):
        step = 5 * res
        end = rrdtool.last(rrd_db)
        start = end - step
        ret = self.rrd_fetch(rrd_db, cf, res, start, end) 
        vals = ret[1]
        l_ = len(vals)
        j = k = -1
        while j >= -l_:
            if vals[j][1] is not None:
                k = j
                break
            j -= 1
        return ret[0], [vals[k]]


    def rrd_fetch(self, rrd_db, cf, res, start, end=None):

        #if not os.path.exists(rrd_db):
            #parent, fname = os.path.split(rrd_db)
            #name = fname.rsplit('.', 1)[0] 
            #raise MonDBException, 'metric %s not found' % name

        #print start, end
        last = rrdtool.last(rrd_db)
        if end is None:
            end = last

        #if start > last:
            #start = end
        #print start, end
        #start = str(start) + "-" + str(res)     # adjust the start/end time to
        #end = str(end) + "-" + str(res)         # match the result with the args
        start = int(start) - int(res)
        end = int(end) - int(res)
        
        data = rrdtool.fetch(rrd_db, cf, "-r", str(res), "-e", str(end), \
                             "-s", str(start))
        logger.debug(r'rrdtool.fetch("%s", "%s", "-r", "%s", "-e", "%s", "-s", "%s")' %\
                (rrd_db, cf, str(res), str(end), str(start)))
        #print data[0]
        #return (data[0][0]+data[0][2], data[0][1], data[0][2]),\
               #data[2][0:-1]
        rstep = data[0][2]
        rstart = data[0][0] + rstep
        rend = data[0][1]
        rdata = []
        timestamp = rstart

        for d in data[2][0:-1]:
            rdata.append((timestamp, d[0]))
            timestamp += rstep

        return (rstart, rend, rstep), rdata

    def push_to_rrd(self, host, name, val):
        dir_path = self._root_dir + os.sep + host
        rrd_db = str(dir_path + os.sep + name + ".rrd")
        #print type(rrd_db)
        
        #r = rrdtool.RoundRobinDatabase(f_path)
        if not os.path.exists(rrd_db):
            ret = self.rrd_create(rrd_db)
        
            if ret:
                print rrdtool.error()   # TODO generic error handler
                sys.exit(1)

        ret = self.rrd_group_updata(rrd_db, val)
        logger.debug('push to %s.rrd: (%s, %s) return %s' %(host, name, val, ret))

        if ret:
            print rrdtool.error()   # TODO generic error handler
            sys.exit(1)

    def rrd_create(self, rrd_db):
        #print type(rrd_db)
        #print "rrd_db:", rrd_db
        parent, fname = os.path.split(rrd_db)
        #print "parent:", parent, "fname:", fname
        name = fname.rsplit('.', 1)[0] 
        #print "metric:", name  
        host = os.path.split(parent)[1]
        #print "host:", host

        try:
            md = self.read(host, "metadata")
        except OSError, e:
            if e.errno == 2:
                #print "no metadata found! write data failed"
                #sys.exit(1)
                raise MonDBException, "no metadata found! write data failed"

        #found = None
        #for metric_group in md:
            #for metric in metric_group['metrics']:
                #if metric['name'] == name:
                    #found = (metric_group, metric)
                    #break
        found = self.get_metric_metricgrp(name, md)

        if found is None:
            raise MonDBException, "metric %s not found" % name
        else:
            metric_group = found[0]
            metric = found[1]
            from time import time
            start = str(int(time()) - 60)
            step = metric_group['period']
            heartbeat = step * 2
            #ds_type = (metric['type'] == 'continuous') and \
                    #"COUNTER" or "GAUGE"
            ds_type = 'GAUGE'
            max = metric.has_key('max') and str(metric["max"]) or "U"
            min = metric.has_key('min') and str(metric["min"]) or "U"
            xxf = '0.5'
            ttl = 3600 * 24
            #cf_time = (step, 3600, ttl)
            cf_time = (step, 60, 600, 900, 1800, 3600)

            return rrdtool.create(rrd_db, "--step", str(step), "--start", start,\
                "DS:%s:%s:%d:%s:%s"%(name,ds_type,heartbeat,min,max),\
                "RRA:AVERAGE:%s:%d:%d"%(xxf,cf_time[0]/step,ttl/cf_time[0]),\
                "RRA:AVERAGE:%s:%d:%d"%(xxf,cf_time[1]/step,ttl/cf_time[1]),\
                "RRA:AVERAGE:%s:%d:%d"%(xxf,cf_time[2]/step,ttl/cf_time[2]),\
                "RRA:AVERAGE:%s:%d:%d"%(xxf,cf_time[3]/step,ttl/cf_time[3]),\
                "RRA:AVERAGE:%s:%d:%d"%(xxf,cf_time[4]/step,ttl/cf_time[4]),\
                "RRA:AVERAGE:%s:%d:%d"%(xxf,cf_time[5]/step,ttl/cf_time[5]),\
                "RRA:MAX:%s:%d:%d"%(xxf,cf_time[1]/step,ttl/cf_time[1]),\
                "RRA:MAX:%s:%d:%d"%(xxf,cf_time[2]/step,ttl/cf_time[2]),\
                "RRA:MAX:%s:%d:%d"%(xxf,cf_time[3]/step,ttl/cf_time[3]),\
                "RRA:MAX:%s:%d:%d"%(xxf,cf_time[4]/step,ttl/cf_time[4]),\
                "RRA:MAX:%s:%d:%d"%(xxf,cf_time[5]/step,ttl/cf_time[5]),\
                "RRA:MIN:%s:%d:%d"%(xxf,cf_time[1]/step,ttl/cf_time[1]),\
                "RRA:MIN:%s:%d:%d"%(xxf,cf_time[2]/step,ttl/cf_time[2]),\
                "RRA:MIN:%s:%d:%d"%(xxf,cf_time[3]/step,ttl/cf_time[3]),\
                "RRA:MIN:%s:%d:%d"%(xxf,cf_time[4]/step,ttl/cf_time[4]),\
                "RRA:MIN:%s:%d:%d"%(xxf,cf_time[5]/step,ttl/cf_time[5]))

    def rrd_update(self, rrd_db, val):
        #timestamp = int(round(val[0])) 
        timestamp = int(val[0])
        val = str(val[1])
        return rrdtool.update(rrd_db, "%d:%s"%(timestamp,val))

    def rrd_group_updata(self, rrd_db, vals):
        ret = 0
        if type(vals) is list:
            for val in vals:
                ret = ret or self.rrd_update(rrd_db, val)
        else:
            ret = self.rrd_update(rrd_db, vals)

    def get_metric_metricgrp(self, name, metadata):
        found = None
        metric_groups = metadata['metric_groups']
        for metric_group in metric_groups:
            for metric in metric_group['metrics']:
                if metric['name'] == name:
                    found = (metric_group, metric)
                    break

        return found

if __name__=="__main__":
    t = MonDBHandler.getInstance("/tmp")
    #print t.read("127.0.0.1", "load_one", step=5, end="1278057380", start="1278057320")
    #print t.read("127.0.0.1", "load_one", step=5)
